#ifndef SQLITE_WRAPPER_H__
#define SQLITE_WRAPPER_H__

#include <string>
#include <vector>
#include <memory>

#include "thirdpart/wxsqlite3/sqlite3.h"
#include "Table.h"
#include "Record.h"

#define SQLITE_WRAPPER_ERROR 1000

using std::string;
using std::vector;
using std::shared_ptr;

namespace duomai {
    namespace im {
        class SQLiteException {
        public:
            SQLiteException(const int error_code,
                            const char* error_message,
                            bool delete_message = true);
            SQLiteException(const SQLiteException& exception);

            virtual ~SQLiteException();
            static const char* ConvertErrorCodeToString(int error_code);
            int error_code()const;
            char* error_message() const;

        private:
            int error_code_;
            char* error_message_;
        };

        class SQLiteStatement {
        private:
            // SQLiteStatement's ctor only to be called by SQLiteWrapper
            friend class SQLiteWrapper;
            SQLiteStatement(string const& statement, sqlite3* db);

        public:
            SQLiteStatement();

            enum dataType {
                INT = SQLITE_INTEGER,
                FLT = SQLITE_FLOAT  ,
                TXT = SQLITE_TEXT   ,
                BLB = SQLITE_BLOB   ,
                NUL = SQLITE_NULL   ,
            };

            dataType DataType(int pos_zero_indexed);

            int         ValueInt   (int pos_zero_indexed);
            string ValueString(int pos_zero_indexed);

            //    SQLiteStatement(const SQLiteStatement&);
            ~SQLiteStatement();

            //SQLiteStatement& operator=(SQLiteStatement const&);

            bool Bind    (int pos_zero_indexed, string const& value);
            bool Bind    (int pos_zero_indexed, double             value);
            bool Bind    (int pos_zero_indexed, int                value);
            bool BindNull(int pos_zero_indexed);

            bool Execute();

            bool NextRow();

            /*   Call Reset if not depending on the NextRow cleaning up.
                 For example for select count(*) statements*/
            bool Reset();

            bool RestartSelect();

        private:
            //void DecreaseRefCounter();

            //int* ref_counter_; // not yet used...
            sqlite3_stmt* stmt_;
        };

        class SQLiteWrapper {
        public:
            SQLiteWrapper();

            bool Open(string const& db_file);
            bool isOpen() const;
            bool Close();

            //bool CreateTable(Table table);

            //bool CreateTable(string table_name,
                                            //vector<string> column_name,
                                            //vector<string> data_type,
                                            //vector<string> flag );

            bool CreateTable(string table_name, ColumnSet *pColumnSet);
            bool insert(string table_name, Record *pRecord);
            bool insert(string table_name, shared_ptr<Record> pRecord);
            bool update(string table_name, Record *pRecord, string where_condition);

            class ResultRecord {
            public:
                vector<string> fields_;
            };

            class ResultTable {
                friend class SQLiteWrapper;
            public:
                ResultTable() : ptr_cur_record_(0) {}

                vector<ResultRecord> records_;

                ResultRecord* next() {
                    if (ptr_cur_record_ < records_.size()) {
                        return &(records_[ptr_cur_record_++]);
                    }
                    return 0;
                }

            private:
                void reset() {
                    records_.clear();
                    ptr_cur_record_=0;
                }

            private:
                unsigned int ptr_cur_record_;
            };

            bool SelectStmt           (string const& stmt, ResultTable& res);
            bool execSQL(string const &stmt);
            SQLiteStatement* Statement(string const& stmt);

            string LastError();

            // Transaction related
            bool Begin   ();
            bool Commit  ();
            bool Rollback();

        private:

            static int SelectCallback(void *p_data, int num_fields, char **p_fields, char **p_col_names);

            bool is_open_;

            sqlite3* db_;
        };

    }
}
#endif
